#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include "sm_trace.h"
#include "itc.h"
#include "xmsg.h"
#include "app_proxy.h"
#include "sm_kapi.h"
#include "common.h"
#include "mmi.h"
#include "ci.h"

#define TASK_MMI_DISPLAY_PRIORITY	80
#define TASK_AID_MMI_DISPLAY		0xE


static unsigned long 	s_isMmiSessOpened;
static short 			s_mmiSsnb;
void openMenuMMI(menu_t *pmenu);
void closeMmi(void);
void openMMIEnqAns(mmiEnq_t *pmmiEnq);
int DisplayTask(void *pvData);
int mmi_init()
{
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_init() - entry\r\n");
	
	/*register_ci_enter_menu(cimmi_enter_menu_event);
	register_ci_close_session(cimmi_close_session);*/
	
	s_isMmiSessOpened=0;
	SM_CreateTask("MmiDisplay", DisplayTask, NULL, NULL, 6*1024, TASK_MMI_DISPLAY_PRIORITY, TRUE, TASK_AID_MMI_DISPLAY);
	SM_TRACE_CRITICAL(TRMOD_MMI, "%s - DisplayTask() is created\n", __FUNCTION__);
	SM_TRACE_RETURN(TRMOD_MMI, "mmi_init() - return 0\r\n");
	return 0;
}


int mmi_display_menu(menu_t * pMenu)
{
	//this function should be implemented by host software
	int i;
	unsigned char choice;
	int ret;

	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_display_menu() - entry\r\n");

	SM_TRACE_INFO(TRMOD_MMI,"mmi_display_menu being called, this function should be implemented by host software\r\n");

	if(pMenu->m_title.m_text && pMenu->m_title.m_len > 0)
	{
		SM_TRACE_NOTIFY(TRMOD_MMI, "title is %s", pMenu->m_title.m_text);
	}

	if(pMenu->m_subtitle.m_text && pMenu->m_subtitle.m_len > 0)
		SM_TRACE_NOTIFY(TRMOD_MMI, "sub title is %s", pMenu->m_subtitle.m_text);

	for(i=0;i<pMenu->m_cnt;i++)
	{
		if(pMenu->m_items[i].m_text)
			SM_TRACE_NOTIFY(TRMOD_MMI, "text is %s", pMenu->m_items[i].m_text);
	}

	if(pMenu->m_btmtitle.m_text && pMenu->m_btmtitle.m_len > 0)
		SM_TRACE_NOTIFY(TRMOD_MMI, "bottom is %s", pMenu->m_btmtitle.m_text);
	
	//主机中显示菜单
	ret = DVBMmiMenuDisplay(pMenu);
	if(ret == -1)
	{
		SM_TRACE_NOTIFY(TRMOD_MMI, "DVBMmiMenuDisplay return -1, cam cancel\r\n");
	}
	else
	{
		choice = (unsigned char)ret;
		SM_TRACE_NOTIFY(TRMOD_MMI, "mmi_display_menu() - choice is %d", choice);
		mmi_enter_choice(choice);
	}

	SM_TRACE_RETURN(TRMOD_MMI, "mmi_display_menu() - return\r\n");
	return 0;
}


void mmi_clear_display(void)
{
	//this function should be implemented by host software
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_clear_display() - entry\r\n");
	
	SM_TRACE_INFO(TRMOD_MMI,"mmi_clear_display being called, this function should be implemented by host software\n");
	DVBMmiCloseDisplay(0);
	SM_TRACE_RETURN(TRMOD_MMI, "mmi_clear_display() - return\r\n");
}


int mmi_display_enquiry(unsigned char* str,
						unsigned char length,
						unsigned char blind_answer,
						unsigned char excepted_answer_length)
{
	mmiEnq_t mmiEnq;
	char answ[256] = {0};
	unsigned char ret = -1;
	
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_display_enquiry() - entry\r\n");

	SM_TRACE_INFO(TRMOD_MMI,"mmi_display_enquiry being called, this function should be implemented by host software\n");
	//主机中显示MMI Enq窗口
	mmiEnq.m_answer_text_len = excepted_answer_length;
	mmiEnq.m_blind_answer = blind_answer;
	mmiEnq.m_enq.m_len = length;
	mmiEnq.m_enq.m_text = str;

	if (str)
		SM_TRACE_NOTIFY(TRMOD_MMI, "mmi_display_enquiry() - str is %s", str);
	SM_TRACE_INFO(TRMOD_MMI,"blind answer: %d\n", blind_answer);
	SM_TRACE_INFO(TRMOD_MMI,"expected answer length: %d\n", excepted_answer_length);

	ret = DVBMmiEnqDisplay(&mmiEnq, answ);
	
	if(ret == 1 && strlen(answ) >0)
		SM_TRACE_NOTIFY(TRMOD_MMI, "mmi_display_enquiry() - answ is %s", answ);

	if(ret == 0 || ret == 1)
		mmi_enter_input(ret, answ, strlen(answ));
	
	SM_TRACE_RETURN(TRMOD_MMI, "mmi_display_enquiry() - return\r\n");
	return 0;
}


int mmi_display_list(menu_t * pMenu)
{
	int		rc;
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_display_list() - entry\r\n");

	rc = mmi_display_menu(pMenu);

	SM_TRACE_RETURN(TRMOD_MMI, "mmi_display_list() - return\r\n");
	return rc;
}


static int on_display_control(short ssnb, unsigned char* pbApduBody, int length)
{
	unsigned char 	buffer[32];
	int				pos = 0;

	SM_TRACE_ENTRY(TRMOD_MMI, "on_display_control() - entry\r\n");

	switch(pbApduBody[0])
	{
	case DCC_SET_MMI_MODE://set mmi mode
		{
			SM_TRACE_INFO(TRMOD_MMI, "on_display_control() - is set mmi mode\r\n");
			switch(pbApduBody[1])
			{
			case MMI_HIGH_LEVEL://high level
				SM_TRACE_INFO(TRMOD_MMI, "on_display_control() - is high level\r\n");
				pos = 0;
				buffer[pos++] = MMI_MODE_ACK;//mmi_mode_ack
				buffer[pos++] = MMI_HIGH_LEVEL;//high level mmi mode
				SM_TRACE_INFO(TRMOD_MMI, "on_display_control() - send display reply\r\n");
				send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_DISPLAY_REPLY, buffer, pos); //display reply
				break;

			default:
				SM_TRACE_INFO(TRMOD_MMI,"on_display_control() - unsupported mmi mode: %d", pbApduBody[1]);
				break;
			}
			break;
		}

	default:
		SM_TRACE_INFO(TRMOD_MMI,"on_display_control() - undisposed display control command: %d", pbApduBody[0]);
		break;
	}

	SM_TRACE_RETURN(TRMOD_MMI, "on_display_control() - return\r\n");
	return 0;
}


static int on_display_reply(short ssnb, unsigned char* pbApduBody, int length)
{
	unsigned char 	buffer[256];
	int				pos = 0;

	SM_TRACE_ENTRY(TRMOD_MMI, "on_display_reply() - entry,ssnb=%d\r\n",ssnb);

	switch(pbApduBody[0])
	{
	case MMI_MODE_ACK://mmi_mode_ack
		{
			SM_TRACE_INFO(TRMOD_MMI, "on_display_reply() - is set mmi mode\r\n");
			switch(pbApduBody[1])
			{
			case MMI_HIGH_LEVEL://high level mmi mode
				{
					unsigned long 	apduLen = 0;
					char* 			choiceMenu[15];
					char			szBuild[50];
					int				mindex = 0;

					SM_TRACE_INFO(TRMOD_MMI, "on_display_reply() - is high level\r\n");
					sprintf(szBuild, "Build: %s %s", __DATE__, __TIME__);
					choiceMenu[mindex++]="CIBOX";
					choiceMenu[mindex++]="Main Menu";
					choiceMenu[mindex++]="'OK': select, 'EXIT': exit";
					choiceMenu[mindex++]=szBuild;
					apduLen = SM_MMIMenu((const unsigned char**)choiceMenu, mindex - 3, buffer);
					//			SM_TRACE_INFO(TRMOD_MMI, "on_display_reply() - send menu object,apduLen=%d\r\n",apduLen);
					//			send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_MENU_LAST, buffer, apduLen); //send menu object

					SM_TRACE_INFO(TRMOD_MMI, "on_display_reply() - set close mmi timer, 10 second\r\n");
					ITC_SetTimerEx(TMID_APP_CLOSEMMI, 5000, ssnb);
					break;
				}

			default:
				SM_TRACE_INFO(TRMOD_MMI,"on_display_reply() - unsupported mmi mode: %d", pbApduBody[1]);
				break;
			}
			break;
		}

	default:
		SM_TRACE_INFO(TRMOD_MMI,"on_display_reply() - undisposed display control command: %d", pbApduBody[0]);
		break;
	}

	SM_TRACE_RETURN(TRMOD_MMI, "on_display_control() - return\r\n");
	return 0;
}

static int on_enquiry(short ssnb, unsigned char* pbApduBody, int length)
{
	SM_TRACE_ENTRY(TRMOD_MMI, "on_enquiry() - entry\r\n");

	mmi_display_enquiry(pbApduBody+2, (unsigned char)(length - 2),
		(unsigned char)(pbApduBody[0]&1), pbApduBody[1]);

	SM_TRACE_RETURN(TRMOD_MMI, "on_enquiry() - return\r\n");
	return 0;
}


static void free_menu(menu_t* pMenu)
{
	int i;

	SM_TRACE_ENTRY(TRMOD_MMI, "free_menu() - entry\r\n");

	if(pMenu->m_title.m_text!=NULL)
	{
		free(pMenu->m_title.m_text);
		pMenu->m_title.m_text=NULL;
	}
	pMenu->m_title.m_len=0;

	if(pMenu->m_subtitle.m_text!=NULL)
	{
		free(pMenu->m_subtitle.m_text);
		pMenu->m_subtitle.m_text=NULL;
	}
	pMenu->m_subtitle.m_len=0;

	if(pMenu->m_btmtitle.m_text!=NULL)
	{
		free(pMenu->m_btmtitle.m_text);
		pMenu->m_btmtitle.m_text=NULL;
	}
	pMenu->m_btmtitle.m_len=0;

	for(i=0;i<pMenu->m_cnt;i++)
	{
		if(pMenu->m_items[i].m_text!=NULL)
		{
			free(pMenu->m_items[i].m_text);
			pMenu->m_items[i].m_text=NULL;
		}
		pMenu->m_items[i].m_len=0;
	}

	SM_TRACE_RETURN(TRMOD_MMI, "free_menu() - return\r\n");
}


static int on_menu_last(short ssnb, unsigned char * pbApdu,
						int length, unsigned char b_menu)
{
	menu_t menu;
	int i;
	unsigned char * p_text;
	unsigned char choice_number;
	unsigned long apduTag;
	unsigned long text_length;

	SM_TRACE_ENTRY(TRMOD_MMI, "on_menu_last() - entry\r\n");

	menu.m_title.m_text=NULL;
	menu.m_title.m_len=0;
	menu.m_subtitle.m_text=NULL;
	menu.m_subtitle.m_len=0;
	menu.m_btmtitle.m_text=NULL;
	menu.m_btmtitle.m_len=0;
	menu.m_cnt=0;
	for(i=0;i<256;i++)
	{
		menu.m_items[i].m_text=NULL;
		menu.m_items[i].m_len=0;
	}

	//get choice_number
	choice_number=pbApdu[0];
	pbApdu+=1;
	length-=1;
	
	//title
	if(length>0)
	{
		p_text=decode_apdu(pbApdu, &apduTag, &text_length);
		if(NULL==p_text)
			return -1;
		length=length-text_length-((unsigned long)p_text-(unsigned long)pbApdu);
		pbApdu=p_text+text_length;
		if(length<0)
		{
			free_menu(&menu);
			return -1;
		}

		if(CI_APDU_TAG_TEXT_LAST==apduTag)
		{
			menu.m_title.m_text=(unsigned char *)malloc(text_length+1);
			if(NULL!=menu.m_title.m_text)
			{
				memcpy(menu.m_title.m_text, p_text, text_length);
				menu.m_title.m_text[text_length]='\0';
				menu.m_title.m_len=text_length;

				//add the customize mmi
			   	 if (0x11 == menu.m_title.m_text[0]) 
				{
					//add the func here
					int ret = 0;
					menu.m_title.m_text++;
					//ret = DVB_mmiSendCustomize(menu.m_title.m_text, menu.m_title.m_len-1);

					free_menu(&menu);
					return ret;
				}
				//end the customize mmi
			}
		}
	}
	else
	{
		free_menu(&menu);
		return -1;
	}

	//subtitle
	if(length>0)
	{
		p_text=decode_apdu(pbApdu, &apduTag, &text_length);
		if(NULL==p_text)
			return -1;
		length=length-text_length-((unsigned long)p_text-(unsigned long)pbApdu);
		pbApdu=p_text+text_length;
		if(length<0)
		{
			free_menu(&menu);
			return -1;
		}

		if(CI_APDU_TAG_TEXT_LAST==apduTag)
		{
			menu.m_subtitle.m_text=(unsigned char *)malloc(text_length+1);
			if(NULL!=menu.m_subtitle.m_text)
			{
				memcpy(menu.m_subtitle.m_text, p_text, text_length);
				menu.m_subtitle.m_text[text_length]='\0';
				menu.m_subtitle.m_len=text_length;
			}
		}
	}
	else
	{
		free_menu(&menu);
		return -1;
	}

	//btmtext
	if(length>0)
	{
		p_text=decode_apdu(pbApdu, &apduTag, &text_length);
		if(NULL==p_text)
			return -1;
		length=length-text_length-((unsigned long)p_text-(unsigned long)pbApdu);
		pbApdu=p_text+text_length;
		if(length<0)
		{
			free_menu(&menu);
			return -1;
		}

		if(CI_APDU_TAG_TEXT_LAST==apduTag)
		{
			menu.m_btmtitle.m_text=(unsigned char *)malloc(text_length+1);
			if(NULL!=menu.m_btmtitle.m_text)
			{
				memcpy(menu.m_btmtitle.m_text, p_text, text_length);
				menu.m_btmtitle.m_text[text_length]='\0';
				menu.m_btmtitle.m_len=text_length;
			}
		}
	}
	else
	{
		free_menu(&menu);
		return -1;
	}

	//menu item
	for(i=0;length>0;i++)
	{
		p_text=decode_apdu(pbApdu, &apduTag, &text_length);
		if(NULL==p_text)
			return -1;
		length=length-text_length-((unsigned long)p_text-(unsigned long)pbApdu);
		pbApdu=p_text+text_length;

		if(length<0)
		{
			free_menu(&menu);
			return -1;
		}

		menu.m_cnt+=1;
		if(CI_APDU_TAG_TEXT_LAST==apduTag)
		{
			menu.m_items[i].m_text=(unsigned char *)malloc(text_length+1);
			if(NULL!=menu.m_items[i].m_text)
			{
				memcpy(menu.m_items[i].m_text, p_text, text_length);
				menu.m_items[i].m_text[text_length]='\0';
				menu.m_items[i].m_len=text_length;
			}
		}
	}

	if(length<0)
	{
		free_menu(&menu);
		return -1;
	}
	
	if(b_menu)
		mmi_display_menu(&menu);
	else
		mmi_display_list(&menu);

	free_menu(&menu);

//	SM_TRACE_ERROR(TRMOD_MMI, "on_menu_last() - return\r\n");

	SM_TRACE_RETURN(TRMOD_MMI, "on_menu_last() - return\r\n");
	return 0;
}

static int on_list_last(short ssnb, unsigned char * pbApdu, int length)
{
	SM_TRACE_ENTRY(TRMOD_MMI, "on_list_last() - entry\r\n");
	return on_menu_last(ssnb, pbApdu, length, 0);
	SM_TRACE_RETURN(TRMOD_MMI, "on_list_last() - return\r\n");
}

int mmi_on_message(xmsg_t* pXmsg)
{
	int		pos = 0;
	xmsg_t		xmsg;
	unsigned short	ssnb;
	unsigned char	tcid;

	SM_ASSERT(pXmsg);
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_on_message() - entry,pXmsg=0x%08X\r\n", pXmsg);

	SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - %s(0x%02X) received\r\n",
		XMSG_NAME(pXmsg->m_type),pXmsg->m_type);

	switch(pXmsg->m_type)
	{
	case APP_OPEN_SS_REQ:
		ssnb = pXmsg->m_bd.m_app_open_ss_req.m_ssnb;
		tcid = pXmsg->m_bd.m_app_open_ss_req.m_tcid;
		SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - APP_OPEN_SS_REQ received, ssnb=%d\r\n", ssnb);
		memset(&xmsg, 0x00, sizeof(xmsg));
		xmsg.m_sndrcver = (RM << 4) | RM;
		xmsg.m_type  = APP_OPEN_SS_RSP;
		xmsg.m_bd.m_app_open_ss_rsp.m_tcid = tcid;
		xmsg.m_bd.m_app_open_ss_rsp.m_ssnb = ssnb;
		xmsg.m_bd.m_app_open_ss_rsp.m_ressId = pXmsg->m_bd.m_app_open_ss_req.m_ressId;
		if(0==s_isMmiSessOpened)
		{
			s_mmiSsnb=ssnb;
			xmsg.m_bd.m_app_open_ss_rsp.m_status = SS_OK;
		}
		else
			xmsg.m_bd.m_app_open_ss_rsp.m_status = SS_BUSY;
		SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - will send APP_OPEN_SS_RSP to PROXY\r\n");
		ITC_SendXmsg(&xmsg);
		s_isMmiSessOpened=1;

		//		SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - set close mmi timer, 10 second\r\n");
		//		ITC_SetTimerEx(TMID_APP_CLOSEMMI, 6000, ssnb);

		break;

	case APP_SS_OPENED:
		break;
	case APP_SS_BROKEN:
		if(s_isMmiSessOpened)
		{
			SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - received APP_SS_BROKEN,kill close_mmi timer\r\n");
			ITC_KillTimer(TMID_APP_CLOSEMMI);

			s_isMmiSessOpened=0;
			mmi_clear_display();
		}
		break;

	case APP_TMPEND:
		{
			ssnb = (unsigned short)pXmsg->m_bd.m_timer.m_param;

			//		SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - is timer event, send close_mmi apdu, ssnb=%d\r\n", ssnb);
			//		buffer[0] = 0; //close mmi immediate
			//		send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_CLOSE_MMI, buffer, 1); //send close_mmi object
			memset(&xmsg, 0x00, sizeof(xmsg));
			xmsg.m_sndrcver = (RM << 4) | RM;
			xmsg.m_type  = APP_CLOSE_SS_REQ;
			xmsg.m_bd.m_app_close_ss_req.m_ssnb = ssnb;
			SM_TRACE_INFO(TRMOD_MMI, "mmi_on_message() - is timer event, will send APP_CLOSE_SS_REQ to PROXY, ssnb=%d\r\n", ssnb);
			ITC_SendXmsg(&xmsg);

			break;
		}

	case APP_APDU:
		{
			unsigned long apduTag = 0;
			unsigned long bodyLen = 0;

			if(pXmsg->m_bd.m_app_apdu.m_pbApdu)
			{
				SM_TRACE_BYTES(TRMOD_MMI, TRLEV_NOTIFY, pXmsg->m_bd.m_app_apdu.m_pbApdu, 3, "pXmsg->m_bd.m_app_apdu.m_pbApdu:");
				decode_apdu(pXmsg->m_bd.m_app_apdu.m_pbApdu, &apduTag, &bodyLen);
				if(apduTag==CI_APDU_TAG_MENU_LAST)
				{
					DVBMmiCloseDisplay(1);//flush
				}
			}

			DVBMmiCloseDisplay(1);//flush
			memset(&xmsg, 0, sizeof(xmsg));
			memcpy(&xmsg, pXmsg, sizeof(xmsg));
			xmsg.m_bd.m_app_apdu.m_pbApdu = malloc(xmsg.m_bd.m_app_apdu.m_apduLen);
			memcpy(xmsg.m_bd.m_app_apdu.m_pbApdu, pXmsg->m_bd.m_app_apdu.m_pbApdu, xmsg.m_bd.m_app_apdu.m_apduLen);
			xmsg.m_sndrcver = RM << 4 | TASK_AID_MMI_DISPLAY;
			ITC_SendXmsg(&xmsg);
			break;
		}
	default:
		break;
	}

	SM_TRACE_RETURN(TRMOD_MMI, "mmi_on_message() - return\r\n");
	return 0;
}


int mmi_enter_input(unsigned char command, unsigned char * str, int length)
{
	//	mmi_enter_input send user's answer to the enquiry to the mudle,
	//	the length of str can exceed 126 bytes.
	//	return value:
	//		0	success
	//		-1	command error
	//		-2 	not enough memory
	//		-4	no application request this message
	unsigned char buffer[256];
	int		pos = 0;

	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_enter_input() - entry\r\n");

	if(command>1)
	{
		return -1;
	}
	else if(0==s_isMmiSessOpened)
	{
		return -4;
	}

	pos = 0;
	if(0==command)
	{
		buffer[pos++]=0;//cancel
	}
	else
	{
		if(length>126)
		{
			SM_TRACE_INFO(TRMOD_MMI,"mmi_enter_input() - input too long");
			length=126;
		}
		buffer[pos++]=1;//answer
		memcpy(&buffer[pos], str, length);
		pos += length;
	}

	SM_TRACE_INFO(TRMOD_MMI, "mmi_enter_input() - send answer\r\n");
	send_app_apdu_to_proxy(s_mmiSsnb, CI_APDU_TAG_ANSW, buffer, pos); //answer

	return 0;
}


int mmi_enter_choice(unsigned char num)
{
	SM_TRACE_ENTRY(TRMOD_MMI, "mmi_enter_choice() - entry\r\n");

	if(0==s_isMmiSessOpened)
	{
		SM_TRACE_INFO(TRMOD_MMI, "mmi_enter_input() - mmi is not opened\r\n");
		return -1;
	}

	SM_TRACE_INFO(TRMOD_MMI, "mmi_enter_choice() - send enter_choice\r\n");
	send_app_apdu_to_proxy(s_mmiSsnb, CI_APDU_TAG_MENU_ANSW, &num, 1); //enter_choice
	return 0;
}


int cimmi_enter_menu_event(void)
{
	int ret = -1;
	SM_TRACE_ERROR(TRMOD_APP, "cimmi_enter_menu_event() - send enter menu\r\n");
	ret = ai_enter_menu(0);
	return ret;
}


int cimmi_close_session(void)
{
	xmsg_t xmsg;
	
	if(s_isMmiSessOpened)
	{
		memset(&xmsg, 0x00, sizeof(xmsg));
		xmsg.m_sndrcver = (RM << 4) | RM;
		xmsg.m_type  = APP_CLOSE_SS_REQ;
		xmsg.m_bd.m_app_close_ss_req.m_ssnb = s_mmiSsnb;
		SM_TRACE_NOTIFY(TRMOD_MMI, "mmi_close_session() - host closes session\r\n");
		ITC_SendXmsg(&xmsg);
		ITC_KillTimer(TMID_APP_CLOSEMMI);
		//mmi_clear_display();
		s_mmiSsnb = 0;
		s_isMmiSessOpened = 0;
	}
	return 0;
}


/****************************************************************************
*
*	APDU field R/W
*
****************************************************************************/

#define CI_ERROR_ARGUMENT 	-1

unsigned long SM_ApduTagWrite(DU*					o_apdu,
							  unsigned long	i_tag)
{
	if (!o_apdu || !o_apdu->m_data)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "SM_ApduTagWrite() - invalid argument\r\n");
		return CI_ERROR_ARGUMENT;
	}

	o_apdu->m_data[o_apdu->m_length++] = (unsigned char)(i_tag>>16);
	o_apdu->m_data[o_apdu->m_length++] = (unsigned char)(i_tag>>8);
	o_apdu->m_data[o_apdu->m_length++] = (unsigned char)i_tag;
	return OK;
}


unsigned long SM_ApduTagRead(const DU*		i_apdu,
							 unsigned long		i_startIndex,
							 unsigned long*			o_endIndex)
{
	unsigned long	dwTag;

	if (!i_apdu || !i_apdu->m_data || !o_endIndex)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "SM_ApduTagRead() - invalid argument\r\n");
		return 0;
	}

	dwTag = 0;

	if (i_startIndex <= i_apdu->m_length)
	{
		dwTag = (unsigned char)i_apdu->m_data[i_startIndex];
		dwTag <<= 8;
		dwTag |= (unsigned char)i_apdu->m_data[i_startIndex + 1];
		dwTag <<= 8;
		dwTag |= (unsigned char)i_apdu->m_data[i_startIndex + 2];

		*o_endIndex = i_startIndex + 2;
	}
	else
		*o_endIndex = i_apdu->m_length;

	return dwTag;
}


unsigned long SM_ApduLengthFieldWrite(DU*					o_apdu,
									  unsigned long	i_length)
{
	unsigned char	b;

	if (!o_apdu || !o_apdu->m_data)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "SM_ApduLengthFieldWrite() - invalid argument\r\n");
		return CI_ERROR_ARGUMENT;
	}

	b = asnWriteApduLengthField((unsigned char*)o_apdu->m_data + o_apdu->m_length, i_length);
	o_apdu->m_length += b;

	return OK;
}


unsigned long SM_ApduLengthFieldRead(const DU*		i_apdu,
									 unsigned long		i_startIndex,
									 unsigned long*			o_endIndex)
{
	unsigned char	b;
	unsigned long	dwLen;

	if (!i_apdu || !i_apdu->m_data || !o_endIndex)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "SM_ApduLengthFieldRead() - invalid argument\r\n");
		return 0;
	}

	dwLen = 0;

	if (i_startIndex < i_apdu->m_length)
	{
		b = asnReadApduLengthField((unsigned char*)i_apdu->m_data + i_startIndex,
			i_apdu->m_length - i_startIndex,
			&dwLen);
		*o_endIndex = i_startIndex + b - 1;
	}
	else
		*o_endIndex = i_apdu->m_length;

	return dwLen;
}


unsigned long appBodyFieldWrite(DU*				o_apdu,
								const void*		i_data,
								const int		i_len)
{
	if (!o_apdu || !o_apdu->m_data)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "appBodyFieldWrite() - invalid argument\r\n");
		return CI_ERROR_ARGUMENT;
	}

	memcpy(o_apdu->m_data + o_apdu->m_length, i_data, i_len);
	o_apdu->m_length += i_len;
	return OK;
}

/****************************************************************************
*
* Application Layer - Man-Machine Interface (MMI)
*
****************************************************************************/

unsigned long appMMIMenu_List(unsigned long			i_sessionNumber,
							  const unsigned char**	i_textList,
							  unsigned char			i_itemNumber,
							  unsigned char*			o_choiceRef,
							  unsigned char*			io_buffer,
							  unsigned long			i_timeout,
							  unsigned long  			dwCtag)
{
	unsigned long		i;
	unsigned long		dwTextLen;
	//	unsigned long		rc;
	//	unsigned long		dwRtag;
	DU					du;

	SM_TRACE_ENTRY(TRMOD_ITC, "appMMIMenu_List() - entry, "
		"session id = %d, i_textList = 0x%08X, "
		"i_itemNumber = %d, o_choiceRef = 0x%08X, io_buffer = 0x%08X, "
		"i_timeout = %d\r\n",
		i_sessionNumber, (int)i_textList, i_itemNumber,
		(int)o_choiceRef, (int)io_buffer, i_timeout);

	if (!io_buffer || !i_textList || !o_choiceRef)
	{
		SM_TRACE_ERROR(TRMOD_ITC, "appMMIMenu_List() - void argument\r\n");
		return -1;
	}

	*o_choiceRef = 0;

	du.m_data = io_buffer;
	du.m_length = 0;

	//	SM_ApduTagWrite(&du, dwCtag);
	//
	//	// calc length field and write it
	//	for (i = 0; i < i_itemNumber + 3; i++)
	//	{
	//  		dwTextLen = strlen(i_textList[i]);
	//		SM_ApduTagWrite(&du, CI_APDU_TAG_TEXT_LAST);
	//		SM_ApduLengthFieldWrite(&du, dwTextLen);
	//		if (dwTextLen > 0)
	//			appBodyFieldWrite(&du, i_textList[i], dwTextLen);
	//	}
	//	dwTextLen = du.m_length - 3 + 1;
	//	du.m_length = 3;
	//	SM_ApduLengthFieldWrite(&du, dwTextLen);
	du.m_data[du.m_length++] = i_itemNumber;

	for (i = 0; i < i_itemNumber + 3; i++)
	{
		dwTextLen = strlen(i_textList[i]);
		SM_ApduTagWrite(&du, CI_APDU_TAG_TEXT_LAST);
		SM_ApduLengthFieldWrite(&du, dwTextLen);
		if (dwTextLen > 0)
		{
			appBodyFieldWrite(&du, i_textList[i], dwTextLen);
			SM_TRACE_INFO(TRMOD_ITC, "appMMIMenu_List() - %s\r\n", i_textList[i]);
		}
	}

	//	rc = SM_SessionWrite(i_sessionNumber, &du);
	//	if (OK != rc)
	//	{
	//		SM_TRACE_ERROR(TRMOD_ITC, "appMMIMenu_List() - session write return error\r\n");
	//		return rc;
	//	}
	//
	//	if (i_timeout)
	//	{
	//		du.m_data = io_buffer;
	//		du.m_length = MAX_APDU_LEN;
	//		rc = SM_SessionRead(i_sessionNumber, &du, i_timeout);
	//		if (OK != rc)
	//		{
	//			if (CI_ERROR_SESSION_NOT_ALLOCATED != rc)
	//				SM_TRACE_ERROR(TRMOD_ITC, "appMMIMenu_List() - session read error, rc=0x%08X\r\n", rc);
	//			return rc;
	//		}
	//
	//		dwRtag = SM_ApduTagRead(&du, 0, &iIndex);
	//		if (CI_APDU_TAG_MENU_ANSW != dwRtag)
	//		{
	//			SM_TRACE_ERROR(TRMOD_ITC, "appMMIMenu_List() - unexpected apdu tag : 0x%06X\r\n", dwRtag);
	//			return CI_ERROR_UNEXPECTED_TAG;
	//		}
	//
	//		*o_choiceRef = du.m_data[4];
	//		SM_TRACE_INFO(TRMOD_ITC, "appMMIMenu_List() - choiceRef=%d\r\n", *o_choiceRef);
	//	}

	SM_TRACE_RETURN(TRMOD_ITC, "appMMIMenu_List() - return,length is %d\r\n", du.m_length);
	return du.m_length;
}


unsigned long SM_MMIMenu(const unsigned char**	i_textList,
						 unsigned  char			i_itemNumber,
						 unsigned char*			io_buffer)
{
	unsigned char			choiceRef;

	return appMMIMenu_List(0, i_textList, i_itemNumber,
		&choiceRef, io_buffer, -1, CI_APDU_TAG_MENU_LAST);
}


int DisplayTask(void *pvData)
{

	unsigned long 	apduTag = 0;
	unsigned long 	bodyLen = 0;
	long 			totalLen = 0;
	unsigned char* 	pbBody = NULL;
	unsigned char* 	pbApdu = NULL;
	int ssnb = 0;
	xmsg_t xmsg, *pXmsg;
	pXmsg = &xmsg;
	SM_TRACE_CRITICAL(TRMOD_MMI, "%s - Display task starts\n", __FUNCTION__);
	if (!ITC_Init(TASK_AID_MMI_DISPLAY))
	{
		SM_TRACE_CRITICAL(TRMOD_MMI, "%s - Init DisplayTask failed\n", __FUNCTION__);
		return 0;
	}
	while (1)
	{
		if (TRUE != ITC_RecvXmsg(pXmsg, ITC_INFINITE))
		{
			SM_TRACE_CRITICAL(TRMOD_MMI, "%s - Receive message failed\n", __FUNCTION__);
			continue;
		}		
		totalLen=pXmsg->m_bd.m_app_apdu.m_apduLen;
		ssnb=pXmsg->m_bd.m_app_apdu.m_ssnb;
		pbApdu = pXmsg->m_bd.m_app_apdu.m_pbApdu;
		for(;totalLen>0;)
		{
			pbBody=decode_apdu(pbApdu, &apduTag, &bodyLen);
			totalLen=totalLen-(((unsigned long)pbBody)-((unsigned long)pbApdu))-bodyLen;
			pbApdu=pbBody+bodyLen;//pbApdu point to the next apdu
			if(totalLen<0)
			{
				SM_TRACE_INFO(TRMOD_MMI,"%s - length error in apdu\n", __FUNCTION__);
				break;
			}
		}
		switch(apduTag)
		{
		case CI_APDU_TAG_DISPLAY_CONTROL://display control
			on_display_control(ssnb, pbBody, bodyLen);
			break;

		case CI_APDU_TAG_ENQ://enquiry
			on_enquiry(ssnb, pbBody, bodyLen);
			ITC_KillTimer(TMID_APP_ENTERMENU);	
			break;

		case CI_APDU_TAG_MENU_LAST://menu last
			{
				on_menu_last(ssnb, pbBody, bodyLen, 1);
				SM_TRACE_INFO(TRMOD_MMI, "%s - menu_last received, kill enter_menu timer\r\n", __FUNCTION__);
				ITC_KillTimer(TMID_APP_ENTERMENU);				
				break;
			}

		case CI_APDU_TAG_LIST_LAST://list last
			on_list_last(ssnb, pbBody, bodyLen);
			break;

		default:
			SM_TRACE_INFO(TRMOD_MMI,"%s - undisposed apduTag: %x\n", __FUNCTION__, apduTag);
			break;
		}//end switch

	}
	
}
